package com.ilxqx.framework.system.controller.base;

import com.google.common.collect.Maps;
import com.ilxqx.framework.security.util.AuthUtils;
import com.ilxqx.framework.system.controller.hook.*;
import com.ilxqx.framework.system.controller.hook.holder.HookHolder;
import com.ilxqx.framework.system.enumeration.EntityEnum;
import com.ilxqx.framework.system.po.ItemEntry;
import com.ilxqx.framework.system.service.SearchableService;
import com.ilxqx.framework.system.util.ExportUtils;
import com.ilxqx.framework.system.util.ReflectionUtils;
import com.ilxqx.framework.system.validator.anno.Validate;
import com.ilxqx.framework.system.validator.group.FrameworkGroups;
import com.ilxqx.framework.util.ResultUtils;
import com.ilxqx.framework.vo.ResultVO;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 框架基本控制器
 * @author venus
 */
@SuppressWarnings("unchecked")
public abstract class AbstractFrameworkController<T, ID, S, U> implements FrameworkController<T, ID, S, U> {

	/**
	 * 具体的服务类由子类进行提供
	 */
	private final SearchableService<T, ID, S> service;

	/**
	 * 注入服务类
	 * @param service 服务类
	 */
	public AbstractFrameworkController(SearchableService<T, ID, S> service) {
		this.service = service;
	}

	/**
	 * 请求一个实体对象或者一个实体对象列表
	 * @param id 实体ID，非必须
	 * @return 一个实体对象或者一个实体对象列表
	 */
	@ApiOperation("获取一个实体或者一个实体列表")
	@GetMapping
	public ResultVO get(@RequestParam(required = false) ID id,
						@PageableDefault(size = 20, sort = "createTime", direction = Sort.Direction.DESC) Pageable pageable,
						S searchForm) {
		if (id != null) {
			// 获取指定id的菜单
			T entity = this.service.get(id).orElse(null);
			// 处理结果
			Object result = this.afterGet(entity);
			return ResultUtils.success("成功", result);
		}

		// 如果需要特别特殊化的需求，可以进行自定义
		if (this.isCustomGetListData()) {
			return ResultUtils.success("成功", this.customGetListData());
		}

		// 判断数据操作权限
		boolean canOperateAllData = AuthUtils.canOperateAllData();
		Page<T> entityPage;
		if (canOperateAllData) {
			entityPage = this.service.getSearchedPage(searchForm, pageable);
		} else {
			entityPage = this.service.getSearchedPage(searchForm, pageable, (ID) AuthUtils.getLoggedUserId());
		}

		// 处理结果
		Object result = this.afterGetList(entityPage);
		return ResultUtils.success("成功", result);
	}

	/**
	 * 自定义list数据获取
	 *
	 * @return 数据
	 */
	@Override
	public Object customGetListData() {
		return service.getList(Sort.by(Sort.Order.desc("createTime")));
	}

	@Validate(FrameworkGroups.Create.class)
	@ApiOperation("创建一个实体")
	@PostMapping
	@Transactional(rollbackFor = Exception.class)
	public ResultVO add(U form) {
		T entity = this.service.create();
		Map<String, Object> processingData = Maps.newHashMap();
		// 做预处理
		for (PreAdd preAdd : HookHolder.preAdds) {
			preAdd.handle(entity, form, processingData);
		}
		// 复制属性
		BeanUtils.copyProperties(form, entity, this.ignoredProperties());
		// 自定义处理
		this.beforeAdd(entity, form);
		// 添加
		T result = this.service.add(entity);
		// 后预处理
		for (PostAdd postAdd : HookHolder.postAdds) {
			postAdd.handle(result, form, processingData);
		}
		// 添加之后的自定义处理
		Object data = this.afterAdd(result, form);
		return ResultUtils.success("添加成功", data);
	}

	@Validate(FrameworkGroups.Modify.class)
	@ApiOperation("修改一个实体")
	@PutMapping
	@Transactional(rollbackFor = Exception.class)
	public ResultVO modify(U form) {
		Object pk = ReflectionUtils.getFormDataPk(form);
		if (pk == null) {
			return ResultUtils.failure("数据ID不存在");
		}
		Optional<T> optionalEntity = this.service.get((ID) pk);
		if (optionalEntity.isEmpty()) {
			return ResultUtils.failure("待修改的数据不存在");
		}
		T entity = optionalEntity.get();
		Map<String, Object> processingData = Maps.newHashMap();
		// 做预处理
		for (PreModify preModify : HookHolder.preModifies) {
			preModify.handle(entity, form, processingData);
		}
		// 复制属性
		BeanUtils.copyProperties(form, entity, this.ignoredProperties());
		// 自定义处理
		this.beforeModify(entity, form);
		// 添加
		T result = this.service.modify(entity);
		// 后预处理
		for (PostModify postModify : HookHolder.postModifies) {
			postModify.handle(result, form, processingData);
		}
		// 添加之后的自定义处理
		Object data = this.afterModify(result, form);
		return ResultUtils.success("修改成功", data);
	}

	@ApiOperation("删除一个实体")
	@DeleteMapping
	@Transactional(rollbackFor = Exception.class)
	public ResultVO delete(@RequestParam ID id) {
		Optional<T> optionalEntity = this.service.get(id);
		if (optionalEntity.isEmpty()) {
			// 主键对应的记录不存在
			return ResultUtils.failure("待删除的数据不存在");
		}
		T entity = optionalEntity.get();
		// 删除前预处理
		for (PreDelete preDelete : HookHolder.preDeletes) {
			preDelete.handle(entity);
		}
		// 调用删除前逻辑
		this.beforeDelete(entity);
		this.service.delete(entity);
		// 删除后预处理
		for (PostDelete postDelete : HookHolder.postDeletes) {
			postDelete.handle(entity);
		}
		// 删除后的处理
		Object data = this.afterDelete(entity);
		return ResultUtils.success("删除成功", data);
	}

	/**
	 * 获取表中字段枚举选项
	 * @return 结果对象
	 */
	@ApiOperation("获取表中字段枚举选项")
	@GetMapping("/enums")
	public ResultVO getFieldsEnumValues() {
		Class<?> cls = this.service.create().getClass();
		Field[] fields = cls.getDeclaredFields();
		Map<String, List<ItemEntry>> enums = Maps.newHashMap();
		for (Field field : fields) {
			if (field.getType().isEnum() && EntityEnum.class.isAssignableFrom(field.getType())) {
				// 是枚举类，获取所有枚举值
				enums.put(field.getName(), EntityEnum.getEnums(field.getType()));
			}
		}
		return ResultUtils.success("成功", enums);
	}

	/**
	 * 根据搜索条件获取excel导出的数据
	 * @param searchForm 搜索表单
	 * @return 结果对象
	 */
	@ApiOperation("根据搜索条件获取excel导出的数据")
	@GetMapping("/export")
	public ResultVO getExportedData(S searchForm) {
		// 判断数据操作权限
		boolean canOperateAllData = AuthUtils.canOperateAllData();
		// 默认只取1000条
		Pageable pageable = PageRequest.of(0, 1000, Sort.by(Sort.Order.desc("createTime")));
		Page<T> entityPage;
		if (canOperateAllData) {
			entityPage = this.service.getSearchedPage(searchForm, pageable);
		} else {
			entityPage = this.service.getSearchedPage(searchForm, pageable, (ID) AuthUtils.getLoggedUserId());
		}
		Map<String, Object> data = Maps.newHashMap();
		data.put("headers", ExportUtils.extractHeaderTitles(this.service.create().getClass()));
		data.put("data", ExportUtils.extractData(entityPage.getContent()));
		return ResultUtils.success("成功", data);
	}

}
